// Broken - invokes alien method from synchronized block! - Page 265
package org.effectivejava.examples.chapter10.item67;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

public class ObservableSet<E> extends ForwardingSet<E> {
	public ObservableSet(Set<E> set) {
		super(set);
	}

	private final List<SetObserver<E>> observers = new ArrayList<SetObserver<E>>();

	public void addObserver(SetObserver<E> observer) {
		synchronized (observers) {
			observers.add(observer);
		}
	}

	public boolean removeObserver(SetObserver<E> observer) {
		synchronized (observers) {
			return observers.remove(observer);
		}
	}

	// This method is the culprit  通知方法
//	private void notifyElementAdded(E element) {
//		synchronized (observers) {
//			for (SetObserver<E> observer : observers) {
//				//调用外部方法
//				observer.added(this, element);
//			}
//		}
//	}

	// Alien method moved outside of synchronized block - open calls - Page 268
	 private void notifyElementAdded(E element) {
		 List<SetObserver<E>> snapshot = null;
		 synchronized(observers) {
			snapshot = new ArrayList<SetObserver<E>>(observers);
		 }

		 for (SetObserver<E> observer : snapshot) {
			 observer.added(this, element);
		 }
	 }

	// Thread-safe observable set with CopyOnWriteArrayList - Page 269
	//
	// private final List<SetObserver<E>> observers =
	// new CopyOnWriteArrayList<SetObserver<E>>();
	//
	// public void addObserver(SetObserver<E> observer) {
	// observers.add(observer);
	// }
	// public boolean removeObserver(SetObserver<E> observer) {
	// return observers.remove(observer);
	// }
	// private void notifyElementAdded(E element) {
	// for (SetObserver<E> observer : observers)
	// observer.added(this, element);
	// }

	@Override
	public boolean add(E element) {
		boolean added = super.add(element);
		if (added) {
			notifyElementAdded(element);
		}
		return added;
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		boolean result = false;
		for (E element : c)
			result |= add(element); // calls notifyElementAdded
		return result;
	}
}
